/*
 * ZenFilter
 * Automagic filter generation plugin for jQuery.
 *
 * Examples and documentation at: http://code.google.com/p/zenfilter/
 * 
 * Copyright (c) 2010 Alejandro García Iglesias
 *
 * Version: 0.1 (18/08/2010)
 * Requires: jQuery v1.3+
 *
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 */
(function ($) {
	$.fn.zenfilter = function (options) {

		// Private properties
		var addFilter,
			classesHash,
			$container,
			defaults,
			$fieldContainer,
			fieldOptions = {'': ''},
			filterTypeOpts,
			formSubmit,
			getFieldDropdown,
			getNewFilter,
			getInputBox,
			getTypeDropdown,
			getRemoveBtn,
			i18n,
			initialize,
			nFilters,
			populate;

		classesHash = {
			addButton: 'zenfilter-addfilter',
			buttonsContainer: 'zenfilter-buttons-container',
			fieldsContainer: 'zenfilter-fields-container',
			incompleteField: 'zenfilter-incomplete',
			individualField: 'zenfilter-field',
			individualInput: 'zenfilter-value',
			individualInputContainer: 'zenfilter-value-container',
			individualType: 'zenfilter-type',
			individualFieldContainer: 'zenfilter-individual-field-container',
			removeButton: 'zenfilter-removefilter',
			submitButton: 'zenfilter-submit'
		};

		defaults = {
			// Options
			action: '',
			classes: {},
			data: [],
			date_format_usa: false,
			isInsideLightbox: false,
			paramName: 'zenfilter',
			populateFilters: [],

			// Events
			onBeforeSubmit: null,
			onFilterAdd: null,
			onFilterRemove: null
		};
		$.extend(defaults, options);
		$.extend(classesHash, defaults.classes);

		
		nFilters = 0;
		$container = $(this).first().empty();
	
		

		// Private methods

		/* Method for internationalization of texts */
		i18n = function (str) {
			if (typeof(zenfilter_i18n) !== 'undefined' && zenfilter_i18n[str]) {
				return zenfilter_i18n[str];
			}

			return str;
		};

		formSubmit = function (event) {
			var doSubmit = true;

			$(this).find('div.' + classesHash.individualFieldContainer).each(function ()
			{
				// Chequear si están vacíos los campos de cada filtro. Si es así, eliminarlo.
				// Si el filtro no tiene alguno de los campos vacíos, indicar que falta ingresar algún dato.
				var $fieldObj = $(this).find('select.' + classesHash.individualField),
					$typeObj  = $(this).find('select.' + classesHash.individualType),
					$inputObj = $(this).find('input.' + classesHash.individualInput);

				if (
					! Boolean($fieldObj.val()) &&
					! Boolean($typeObj.val()) &&
					! Boolean($inputObj.val()))
				{
					$(this).detach();
				}
				else if (
					! Boolean($fieldObj.val()) ||
					! Boolean($typeObj.val()) ||
					! Boolean($inputObj.val()))
				{
					if (! Boolean($fieldObj.val())) {
						$fieldObj.addClass(classesHash.incompleteField);
					}
					if (! Boolean($typeObj.val())) {
						$typeObj.addClass(classesHash.incompleteField);
					}
					if (! Boolean($inputObj.val())) {
						$inputObj.addClass(classesHash.incompleteField);
					}

					doSubmit = false;
				}
			});

			if (doSubmit)
			{
				$(this).find('select.' + classesHash.individualField).removeAttr('name');   // This is to prevent this field's value to being sent on the submit

				if ($.isFunction(defaults.onBeforeSubmit)) {
					defaults.onBeforeSubmit.call(this);
				}
			}

			return doSubmit;
		};

		/* Initialization method */
		initialize = function () {
			var $form;

			// Check for default options that must be set to work.
			if (defaults.data.length < 1) {
				throw new Error('ZenFilter error: Field data is not provided. Please read documentation on initialising the plugin.');
			}

			$.each(defaults.data, function(i, item){
				fieldOptions[i] = item.label;
			});

			filterTypeOpts = {
				'date': {
					'': '',
					after: i18n('after'),
					before: i18n('before'),
					between: i18n('between')
				},
				'numeric': {
					'': '',
					between: i18n('between'),
					equals: i18n('equal to'),
					greater: i18n('greater than'),
					less: i18n('less than'),
					notequals: i18n('other than')
				},
				'string': {
					'': '',
					begins: i18n('begins with'),
					ends: i18n('ends with'),
					equals: i18n('equal to'),
					like: i18n('contains'),
					notlike: i18n('does not contain')
				},
				'time': {
					between: i18n('between')
				}
			};

			// Create form
			$form = $('<form action="' + defaults.action + '" method="get"><div class="' + classesHash.fieldsContainer + '"></div><div class="' + classesHash.buttonsContainer + '"><button type="submit" class="' + classesHash.submitButton + '" title="' + i18n('Filter') + '" value="' + i18n('Filter') + '">' + i18n('Filter') + '</button><button type="button" class="' + classesHash.addButton + '" title="' + i18n('Add filter') + '" value="' + i18n('Add filter') + '">' + i18n('Add filter') + '</button></div>');
			$form.delegate('button.' + classesHash.addButton, 'click', addFilter).submit(formSubmit).appendTo($container);

			$fieldContainer = $('div.' + classesHash.fieldsContainer);

			// Initialised. Now...
			if (defaults.populateFilters.length < 1) {	// If the 'populateFilters' option is not set...
				addFilter();	// Add the first empty filter
			}
			else {	// Else...
				populateFilters();	// Populate filters
			}
		};

		/* Obtain a dropdown for the field select */
		getFieldDropdown = function (populateData) {
			var $hiddenInput, i, $obj;

			$hiddenInput = $('<input>', {
				name: defaults.paramName + '[' + nFilters + '][field]',
				type: 'hidden'
			});
			$obj = $('<select></select>', {
					'class': classesHash.individualField,
					name: defaults.paramName + '[' + nFilters + '][fakefield]'
				}).
				data('zenfilter', { fieldId: nFilters }).
				addOption(fieldOptions).
				sortOptions(true).
				selectOptions('').
				change(function(){
					$(this).nextAll('select, div.' + classesHash.individualInputContainer).detach();
					$(this).removeClass(classesHash.incompleteField).after(getTypeDropdown($(this).data('zenfilter').fieldId, $(this).val()));
					$(this).prev('input[type=hidden]').val(defaults.data[$(this).val()].fieldname);
				});
			if (populateData) {
				for (i = 0; i < defaults.data.length; i += 1) {
					if (defaults.data[i].fieldname === populateData) {
						$obj.selectOptions(String(i));
						$hiddenInput.val(defaults.data[$obj.val()].fieldname);
					}
				}
			}

			return $hiddenInput.add($obj);
		};

		/* Get a filter wrapper for a new field */
		getNewFilter = function () {
			var $newFilter;

			$newFilter = $('<div></div>', {
					'class': classesHash.individualFieldContainer
				}).
				delegate('a.' + classesHash.removeButton, 'click', function () {
					$(this).parent().detach();
	
					if ($.isFunction(defaults.onFilterRemove)) {
						defaults.onFilterRemove.call(this);
					}
				});

			return $newFilter;
		};

		/* Obtain a input field */
		getInputBox = function (fieldId, fieldType, fieldSubtype, populateData) {
			var $obj;

			$obj = $('<div></div>', {
				'class': classesHash.individualInputContainer
			});
			$obj.append(
				$('<input />', {
					'class': classesHash.individualInput + ' ' + classesHash.individualInput + '-' + fieldType + (fieldSubtype === 'between' ? '-start' : ''),
					name: defaults.paramName + '[' + fieldId + '][value]' + (fieldSubtype === 'between' ? '[start]' : ''),
					type: 'text',
					val: (populateData ? (fieldSubtype === 'between' ? populateData.start : populateData) : null)
				}));
			if (fieldSubtype === 'between') {
				$obj.append(' ' + i18n('and') + ' ').append(
					$('<input />', {
						'class': classesHash.individualInput + ' ' + classesHash.individualInput + '-' + fieldType + '-end',
						name: defaults.paramName + '[' + fieldId + '][value][end]',
						type: 'text',
						val: (populateData ? populateData.end : null)
					}));
			}
			$obj.delegate('input', 'keypress', function () {
				$(this).removeClass(classesHash.incompleteField);
			});
			// Apply corresponding plugins to the input
			$obj.find('input.' + classesHash.individualInput + '-date').calendricalDate({ relativePositioning: defaults.isInsideLightbox, usa: defaults.date_format_usa });
			$obj.find('input.' + classesHash.individualInput + '-date-start, input.' + classesHash.individualInput + '-date-end').calendricalDateRange({ relativePositioning: defaults.isInsideLightbox, usa: defaults.date_format_usa });
			$obj.find('input.' + classesHash.individualInput + '-numeric').numeric();
			$obj.find('input.' + classesHash.individualInput + '-time').calendricalTime({ relativePositioning: defaults.isInsideLightbox, usa: defaults.date_format_usa });
			$obj.find('input.' + classesHash.individualInput + '-time-start, input.' + classesHash.individualInput + '-time-end').calendricalTimeRange({ relativePositioning: defaults.isInsideLightbox, usa: defaults.date_format_usa });
				
			return $obj;
		};

		/* Obtain a dropdown for the type select */
		getTypeDropdown = function (fieldId, colId, populateData) {
			var $obj, type;

			type = defaults.data[colId].type;
			$obj = $('<select></select>', {
					'class': classesHash.individualType,
					name: defaults.paramName + '[' + fieldId + '][type]'
				}).
				data('zenfilter', { fieldId: fieldId, fieldType: type }).
				addOption(filterTypeOpts[type]).
				sortOptions(true).
				selectOptions('').
				change(function(){
					$(this).next('div.' + classesHash.individualInputContainer).detach();
					$(this).removeClass(classesHash.incompleteField).after(getInputBox($(this).data('zenfilter').fieldId, $(this).data('zenfilter').fieldType, $(this).val()));
				});

			if (populateData) {
				$obj.selectOptions(populateData);
			} else if ( ! $obj.containsOption('')) {
				var t = setTimeout(function(){
					$obj.trigger('change');
				}, 10);
			}

			return $obj;
		};

		/* Get a remove field button */
		getRemoveBtn = function () {
			return $('<a></a>', {
				'class': classesHash.removeButton,
				title: i18n('Remove filter'),
				text: i18n('Remove filter'),

				click: function () {
					$(this).parent().detach();
		
					if ($.isFunction(defaults.onFilterRemove)) {
						defaults.onFilterRemove.call(this);
					}
				}
			});
		};

		/* Adds a filter to the container */
		addFilter = function (fieldData) {
			var $fieldDropdown, $newFilter;

			// Create the filter container
			$newFilter = getNewFilter();

			// Append the field dropdown
			$fieldDropdown = (fieldData ? getFieldDropdown(fieldData.field) : getFieldDropdown());
			$newFilter.append($fieldDropdown);
			
			// Append the remove button
			$newFilter.append(getRemoveBtn());

			// Append the new filter to the container
			$newFilter.appendTo($fieldContainer);

			// Increment the filter counter
			nFilters += 1;

			if ($.isFunction(defaults.onFilterAdd)) {
				defaults.onFilterAdd.call(this);
			}
		};
		
		/* Populate the filters with the data provided */
		populateFilters = function () {
			var $fieldDropdown,
				$inputBox,
				$newFilter,
				$typeDropdown;

			$.each(defaults.populateFilters, function (i, fieldData)
			{
				// Filter container
				$newFilter = getNewFilter();

				// Field dropdown
				$fieldDropdown = getFieldDropdown(fieldData.field);
				$newFilter.append($fieldDropdown);
			
				// Type dropdown
				$typeDropdown = getTypeDropdown($fieldDropdown.last().data('zenfilter').fieldId, $fieldDropdown.last().val(), fieldData.type);
				$newFilter.append($typeDropdown);

				// Input value
				$inputBox = getInputBox($typeDropdown.data('zenfilter').fieldId, $typeDropdown.data('zenfilter').fieldType, $typeDropdown.val(), fieldData.value);
				$newFilter.append($inputBox);

				// Remove button
				$newFilter.append(getRemoveBtn());

				$newFilter.appendTo($fieldContainer);

				// Increment the filter counter
				nFilters += 1;
			});
		};


		// Init
		initialize();


		return $(this);
	};
}(jQuery));
